// FILE: Chip8Emulator.cpp by Richard Hughes 27/04/2012
//
// DESC: Implements the Chip8 Emulator

// INCLUDES
#include "Chip8Emulator.h"

namespace Emulator
{
namespace Chip8
{

/*************************************************************/
//
// CLASS: Chip8Emulator
//
// DESC: The Chip 8 Emulator definition
//
/*************************************************************/

// constructor
// takes nothing
// returns nothing
Chip8Emulator::Chip8Emulator()
{
	this->_executedCode = "";
	this->_running = true;

	this->_runningMode = EmulatorRunningMode_SuperChip8;
}

// destructor
// takes nothing
// returns nothing
Chip8Emulator::~Chip8Emulator()
{
}

// init this emulator
// takes nothing
// returns true upon success, false otherwise
bool Chip8Emulator::Init()
{
	this->_cpu.SetEmulator(this);

	this->_debugger.SetStep(BASE_WRITABLE);

	return true;
} // Init

// runs once per 'frame' on this host machine - thus the main loop
// takes nothing
// returns true upon success, false otherwise
bool Chip8Emulator::Frame()
{
	uint16_t op = 0;

	// TODO: add this debugger code either into the debugger class, or integrate with the CPU code maybe?
	// probably best in the debugger code - seperation of concerns
	if(this->_debugger.IsAttached())
	{
		// at the moment assuming only one breakpoint...
		if(this->_cpu.GetPC() != this->_debugger.GetBreakpoints()[0] && !this->_debugger.IsStepping())
			op = this->_cpu.Tick(&this->_ram);
	}
	else
	{
		op = this->_cpu.Tick(&this->_ram);
	}

	this->_debugger.SetStep(this->_cpu.GetPC());

	// shall we attach or detach?
	if(this->_debugger.IsAttached())
	{
		if(this->_keyboard->DetachDebugger())
			this->_debugger.Continue();

		if(this->_keyboard->DebuggerStep())
			this->_debugger.Step(&this->_cpu, &this->_ram);
	}
	else
	{
		if(this->_keyboard->AttachDebugger() || this->_cpu.GetPC() == this->_debugger.GetBreakpoints()[0])
			this->_debugger.Break();
	}

	//if debug...
	uint16_t swap = (op << 8) | (op >> 8); // need to swap the bytes for some reason as the below caste changes it...? TODO: find out why... possibly endianness...?
	this->_executedCode += this->_disassembler.Disassemble((uint8_t*)&swap, 2); // this expects the ops in bytes, rather than in 16 bits, so two bytes are being passed in
	// end if

	return this->_running;
} // Frame

// shutsdown this emulator
// takes nothing
// returns true upon success, false otherwise
bool Chip8Emulator::Shutdown()
{
	bool res = false;

	res |= !this->_rom.UnLoad();

	this->_cpu.SetEmulator(NULL);

	return res;
} // Shutdown

// loads the ROM
// takes the filename of the ROM to load
// returns true upon success, else returns false
bool Chip8Emulator::LoadROM(string fileName)
{
	this->_rom.Load(fileName);

	this->_ram.Reset();

	this->_ram.Write((byte*)this->_rom.GetRawData(), this->_ram.GetBaseWritable(), this->_rom.Size());

	return true;

} // LoadROM

// sets the emulator into Chip8 mode
// takes nothing
// returns nothing
void Chip8Emulator::EnterChip8Mode()
{
	this->_runningMode = EmulatorRunningMode_Chip8;

	this->_display->SetWidth(64);
	this->_display->SetHeight(32);
} // end EnterChip8Mode

// sets the emulator into SuperChip8 mode
// takes nothing
// returns nothing
void Chip8Emulator::EnterSuperChip8Mode()
{
	this->_runningMode = EmulatorRunningMode_SuperChip8;

	this->_display->SetWidth(128);
	this->_display->SetHeight(64);
} // end EnterSuperChip8Mode

// sets the emulator into MegaChip8 mode
// takes nothing
// returns nothing
void Chip8Emulator::EnterMegaChip8Mode()
{
	this->_runningMode = EmulatorRunningMode_MegaChip8;
} // end EnterMegaChip8Mode

} // end namespace Chip8
} // end namespace Emulator

// EOF: Chip8Emulator.cpp